﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LINQToolKit.Collections
{
    public interface IEnumerableCollection : IEnumerable, IEnumerator
    {
        int Count();

        void Insert(int index, IEnumerable instance);
        void Add(IEnumerable instance);
    }

    /// <summary>
    /// Represents combination for the IEnumerables
    /// </summary>
    /// <remarks>
    /// This class is not thread-safe
    /// </remarks>
    /// <typeparam name="T"></typeparam>
    public sealed class Enumerables<T> : IEnumerableCollection, IEnumerator<T>, IEnumerable<T>
    {
        private IList<IEnumerable<T>> _instances;
        private int _currentIndex;
        private IEnumerator<T> _currentEnumerator;

        public int Count(Func<T, bool> predicate)
        {
            return this._instances.Select(i => i.Count(predicate)).Sum();
        }

        public int Count()
        {
            return this._instances.Select(i => i.Count()).Sum();
        }

        public Enumerables()
        {
            this._instances = new List<IEnumerable<T>>();
        }

        public void Add(IEnumerable instance)
        {
            this.Add((IEnumerable<T>)instance);
        }

        public void Insert(int index, IEnumerable instance)
        {
            this.Insert(index, (IEnumerable<T>)instance);
        }

        public void Insert(int index, IEnumerable<T> instance)
        {
            if (_instances == null)
            {
                throw new ArgumentNullException("enumerable");
            }

            this._instances.Insert(index, instance);

            if (this._currentEnumerator == null)
            {
                this._currentIndex = 0;
                this._currentEnumerator = this._instances[0].GetEnumerator();
            }
        }

        public void Add(IEnumerable<T> instance)
        {
            if (_instances == null)
            {
                throw new ArgumentNullException("enumerable");
            }

            this._instances.Add(instance);

            if (this._currentEnumerator == null)
            {
                this._currentIndex = 0;
                this._currentEnumerator = this._instances[0].GetEnumerator();
            }
        }

        public bool MoveNext()
        {
            if (this._currentEnumerator == null)
            {
                return false;
            }

            while (!this._currentEnumerator.MoveNext())
            {
                this._currentIndex++;

                if (this._currentIndex < this._instances.Count)
                {
                    this._currentEnumerator = this._instances[this._currentIndex].GetEnumerator();   
                }
                else
                {
                    return false;
                }
            }

            return true;
        }

        public void Reset()
        {
            this._currentIndex = 0;

            if (this._instances.Count > 0)
            {
                this._currentEnumerator = this._instances[0].GetEnumerator();
            }
            else
            {
                this._currentEnumerator = null;
            }
        }

        public IEnumerator GetEnumerator()
        {
            return this;
        }

        public void Dispose()
        {
            foreach (var item in this._instances)
            {
                item.GetEnumerator().Dispose();
            }
        }

        IEnumerator<T> IEnumerable<T>.GetEnumerator()
        {
            return this;
        }

        public T Current
        {
            get 
            {

                if (this._currentEnumerator == null)
                {
                    throw new ArgumentNullException("_currentEnumerator");
                }

                return this._currentEnumerator.Current;
            }
        }

        object IEnumerator.Current
        {
            get { return this.Current; }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }
    }
}